#include "vds.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>

#define MAX_STACK_COUNT 512
/* #define VDS_DEBUG */

typedef enum{
    E_NORMAL,
    E_PATH_INVALID,
    E_MEM_OUT,
    E_PATH_TOO_LONG,
    E_ACCESS_DENY,
    E_OUTPUT_ARRAY_TOO_SMALL,
    E_DIR_NAME_INVALID,
    E_DIR_EXIST,
    E_MKDIR_ERROR,
    E_DIR_NOT_EMPTY,//目录非空
    E_RMDIR_ERROR//目录删除失败
}ERROR_TYPE;

// 上一个字符串结束下标的栈 数组模拟栈 最大512个元素
typedef struct stack_s{
    int idx_arr[MAX_STACK_COUNT];
    size_t size;
}stack_t;

typedef struct vds_s{
    char path[PATH_MAX];
    stack_t stack;
    int baseIdx;
    ERROR_TYPE error_type;
}vds_t;

int top_stack(const stack_t *stack){
    return stack->idx_arr[stack->size-1];
}

int is_empty_stack(const stack_t *stack){
    return stack->size==0;
}

int pop_stack(stack_t *stack){
    stack->size--;
    return 0;
}

int push_stack(stack_t *stack,int idx){
    stack->idx_arr[stack->size]=idx;
    stack->size++;
    return 0;
}

int free_stack(stack_t *stack){
    while(!is_empty_stack(stack)){
        pop_stack(stack);
    }
    return 0;
}

vds_t* vds_init(const char *root){
    vds_t *vds=(vds_t *)calloc(1,sizeof(vds_t));
    if(vds==NULL){
        return vds;   
    }

    if(realpath(root,vds->path)==NULL){
        free(vds);
        return NULL;
    }
    vds->baseIdx = strlen(vds->path);
    push_stack(&vds->stack,vds->baseIdx);

#ifdef VDS_DEBUG
    printf("debug | init root path: %s\n",vds->path);
#endif
    return vds;
}

int vds_cd(vds_t *vds,const char *path){
    // 检查路径长度是否合法
    size_t path_len=strlen(path);
    if(path_len==0){
        vds->error_type=E_PATH_INVALID;
        return -1;
    }
    if(path_len>=PATH_MAX){
        vds->error_type=E_PATH_TOO_LONG;
        return -1;
    }
    // 制作路径和栈的副本
    char path_tmp[PATH_MAX];
    memcpy(path_tmp,vds->path,sizeof(path_tmp));
    stack_t stack_tmp;
    memcpy(&stack_tmp,&vds->stack,sizeof(stack_tmp));

    char str[1024]={0};
    strncpy(str,path,sizeof(str)-1);
    char *token,*rest=str;

    if(str[0]=='/'){
        while(stack_tmp.size>1){
            pop_stack(&stack_tmp);
        }
        path_tmp[top_stack(&stack_tmp)]='\0';
    }
    while((token=strtok_r(rest,"/",&rest))){
        if(strcmp(token,"..")==0){
            if(stack_tmp.size>1){
                pop_stack(&stack_tmp);
                path_tmp[top_stack(&stack_tmp)]='\0';
            }else{
                vds->error_type=E_PATH_INVALID;
                return -1;
            }
        }else if(strcmp(token,".")==0){
            // do nothing.
        }else{
            if(stack_tmp.size==MAX_STACK_COUNT){
                vds->error_type=E_PATH_TOO_LONG;
            }else{
                int idx=top_stack(&stack_tmp);
                path_tmp[idx++]='/';
                strcpy(path_tmp+idx,token);
                push_stack(&stack_tmp,idx+strlen(token));
            }
        }
#ifdef VDS_DEBUG
        printf("debug | vds_cd token= %s\n",token);
        printf("debug | path_tmp=%s\n",path_tmp);
#endif
    }
    
    // 验证路径是否合法
    DIR *dir = opendir(path_tmp);
    if(dir){
        closedir(dir);
    }else if(errno==ENOENT){
        vds->error_type=E_PATH_INVALID;
        return -1;
    }else{
        vds->error_type=E_ACCESS_DENY;
        return -1;
    }

    memcpy(vds->path,path_tmp,sizeof(path_tmp));
    memcpy(&vds->stack,&stack_tmp,sizeof(stack_tmp));
    return 0;
}

char *vds_pwd(vds_t *vds,char *path){
    if(path==NULL){
        path=(char*)malloc(PATH_MAX);
    }
    if(vds->stack.size==1){
        path[0]='/';
        path[1]='\0';
        return path;
    }
    
    strcpy(path,vds->path+vds->baseIdx);
    return path;
}

int vds_ls(vds_t *vds,const char *path,char *output,size_t output_size){
    char current_path[PATH_MAX];
    if(vds_real_path(vds,path,current_path)==NULL){
        vds->error_type=E_PATH_INVALID;
        return -1;
    }

    DIR *dir=opendir(current_path);
    if(dir==NULL){
        if(errno==ENOENT){
            vds->error_type=E_PATH_INVALID;
            return -1;
        }else{
            vds->error_type=E_ACCESS_DENY;
            return -1;
        }
    }

    struct dirent *entry;
    size_t current_len=0;
    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_name[0] == '.') continue;
        size_t remaining=output_size-current_len;
        int written =snprintf(output+current_len,remaining,"%s  ",entry->d_name);
        if(written<0){
            closedir(dir);
            vds->error_type=E_OUTPUT_ARRAY_TOO_SMALL;
            return -1;
        }
        size_t expected_len=current_len+written;
        if(expected_len>=output_size){
            current_len=strlen(output)-1;
            break;
        }
        current_len=expected_len;
    }
    output[current_len]='\0';
    closedir(dir);
    return 0;
}

int vds_mkdir(vds_t *vds,const char *dir_name){
    if(strlen(dir_name)==0){
        vds->error_type=E_DIR_NAME_INVALID;
        return -1;
    }
    
    char path[PATH_MAX];
    if(vds_real_path(vds,dir_name,path)==NULL){
        vds->error_type=E_PATH_INVALID;
        return -1;
    }
    int ret = mkdir(path,0755);

    if(ret==-1){
        switch(errno){
        case EEXIST:
            vds->error_type=E_DIR_EXIST;
            break;
        case ENOENT:
            vds->error_type=E_PATH_INVALID;
            break;
        case EACCES:
            vds->baseIdx=E_ACCESS_DENY;
            break;
        default :
            vds->error_type=E_MKDIR_ERROR;
        }
        return -1;
    }
    return 0;
}

int vds_rmdir(vds_t *vds,const char *dir_name){
    DIR *dir;
    struct dirent *entry;
    struct stat st;
    char path[PATH_MAX];
    //检查目录是否存在
    char current_path[PATH_MAX];
    if(vds_real_path(vds,dir_name,current_path)==NULL){
        vds->error_type=E_PATH_INVALID;
        return -1;
    }

    if(access(dir_name,F_OK)!=0){
        vds->error_type=E_PATH_INVALID;
        return -1;
    }
    //获取目录属性
    if(stat(dir_name,&st)<0){
        vds->error_type=(errno==EACCES)?E_ACCESS_DENY:E_PATH_INVALID;
        return -1;
    }
    //如果是普通文件，直接删除
    if(S_ISREG(st.st_mode)){
        if(unlink(dir_name)!=0){
        vds->error_type=(errno==EACCES)?E_ACCESS_DENY:E_RMDIR_ERROR;
            return -1;
        }
        return 0;
    }//打开目录
    if(!(dir = opendir(dir_name))){
        vds->error_type=(errno=EACCES)?E_ACCESS_DENY:E_PATH_INVALID;
        return -1;
    }
    //递归处理目录项
    while((entry=readdir(dir))!=NULL){
        if(strcmp(entry->d_name,".")==0||strcmp(entry->d_name,".."));
        continue;
        //构建完整路径
        char sub_path[PATH_MAX];
        snprintf(sub_path,sizeof(sub_path),"%s/%s",path,entry->d_name);
        //递归处理子项
        if(vds_rmdir(vds,sub_path)!=0){
            closedir(dir);
            return -1;
        }
    }
    closedir(dir);
    //删除空目录
    if(rmdir(dir_name)!=0){
        switch (errno) {
            case ENOTEMPTY:
                vds->error_type = E_DIR_NOT_EMPTY;
                break;
            case EACCES:
                vds->error_type = E_ACCESS_DENY;
                break;
            default:
                vds->error_type = E_RMDIR_ERROR;
        }
        return -1;
    }
    return 0;
}
int vds_remove(vds_t *vds,const char *file_name){
    char current_path[PATH_MAX];
    if(vds_real_path(vds,file_name,current_path)==NULL){
         vds->error_type=E_PATH_INVALID;
          return -1;
     }
    struct stat st;
    if(stat(current_path,&st)!=0){
        switch(errno){
        case EACCES:
            vds->error_type=E_ACCESS_DENY;
            break;
        case ENOENT:
            vds->error_type=E_PATH_INVALID;
            break;
        default:
            vds->error_type=E_RMDIR_ERROR;
        }
        return -1;
    }
    if(!S_ISREG(st.st_mode)){
        vds->error_type=E_PATH_INVALID;
        return -1;
    }
    if(unlink(current_path)!=0){
        switch(errno){
        case EACCES:
            vds->error_type=E_ACCESS_DENY;
            break;
        case ENOENT:
            vds->error_type=E_PATH_INVALID;
            break;
        default:
            vds->error_type=E_RMDIR_ERROR;
        }
        return -1;
    }

return 0;    
}
char *vds_real_path(vds_t *vds,const char *path,char *resolved_path){
    if(strlen(path)==0){
        vds->error_type=E_PATH_INVALID;
        return NULL;
    }

    if(resolved_path==NULL){
        if((resolved_path=(char*)malloc(PATH_MAX))==NULL){
            vds->error_type=E_MEM_OUT;
            return NULL;
        }
    }
    // 制作路径和栈的副本
    char path_tmp[PATH_MAX];
    memcpy(path_tmp,vds->path,sizeof(path_tmp));
    stack_t stack_tmp;
    memcpy(&stack_tmp,&vds->stack,sizeof(stack_tmp));

    char str[1024]={0};
    strncpy(str,path,sizeof(str)-1);
    char *token,*rest=str;

    if(str[0]=='/'){
        while(stack_tmp.size>1){
            pop_stack(&stack_tmp);
        }
        path_tmp[top_stack(&stack_tmp)]='\0';
    }
    while((token=strtok_r(rest,"/",&rest))){
        if(strcmp(token,"..")==0){
            if(stack_tmp.size>1){
                pop_stack(&stack_tmp);
                path_tmp[top_stack(&stack_tmp)]='\0';
            }else{
                vds->error_type=E_PATH_INVALID;
                return NULL;
            }
        }else if(strcmp(token,".")==0){
            // do nothing.
        }else{
            if(stack_tmp.size==MAX_STACK_COUNT){
                vds->error_type=E_PATH_TOO_LONG;
                return NULL;
            }else{
                size_t idx=top_stack(&stack_tmp);
                path_tmp[idx++]='/';
                strcpy(path_tmp+idx,token);
                push_stack(&stack_tmp,idx+strlen(token));
            }
        }
#ifdef VDS_DEBUG
        printf(" vds_real_path token= %s\n",token);
        printf(" path_tmp=%s\n",path_tmp);
#endif
    }
    strcpy(resolved_path,path_tmp);
    return resolved_path;
}

int vds_free(vds_t *vds){
    free_stack(&vds->stack);
    free(vds);
    return 0;
}

char normal_str[]="vds is normal, no error.";
char memout_str[]="memory is run out of.";
char path_too_long_str[]="path is to long to store.";
char path_invalid_str[]="path is invalid.";
char access_deny_str[]="access to directory is deny.";
char output_array_too_small_str[]="output_array is to small to store result.";
char dir_name_invalid_str[]="directory name is invalid.";
char dir_exist_str[]="directory is exist.";
char dir_mkdir_error_str[]="mkdir error.";
char dir_not_empty_str[]="Directory not empty";
char dir_rmdir_error_str[]="Failed to remove directory";
const char *vds_error(const vds_t *vds){
    if(vds==NULL){
        return NULL;
    }
    char *str;
    switch(vds->error_type){
        case E_NORMAL: str=normal_str;break;
        case E_MEM_OUT: str=memout_str;break;
        case E_PATH_TOO_LONG: str=path_too_long_str;break;
        case E_PATH_INVALID: str=path_invalid_str;break;
        case E_ACCESS_DENY: str=access_deny_str;break;
        case E_OUTPUT_ARRAY_TOO_SMALL: str=output_array_too_small_str;break;
        case E_DIR_NAME_INVALID: str=dir_name_invalid_str;break;
        case E_DIR_EXIST: str=dir_exist_str;break;
        case E_MKDIR_ERROR: str=dir_mkdir_error_str;break;
        case E_DIR_NOT_EMPTY:str=dir_not_empty_str;break;
        case E_RMDIR_ERROR:str=dir_rmdir_error_str;break;
        default:str=normal_str;break;
    }

    return str;
}
